package com.mazaiting;

import com.mazaiting.classfile.attr.MemberInfo;
import com.mazaiting.classfile.attr.info.CodeAttribute;
import com.mazaiting.instructions.InstructionFactory;
import com.mazaiting.instructions.base.BytecodeReader;
import com.mazaiting.instructions.base.Instruction;
import com.mazaiting.rtda.MtFrame;
import com.mazaiting.rtda.MtThread;
import com.mazaiting.rtda.heap.MtMethod;
import lombok.extern.slf4j.Slf4j;

/**
 * 解释器
 *
 * @author mazaiting
 * @since 2021/8/15
 */
@Slf4j
public class Interpreter {

    /**
     * 解释器
     *
     * @param method  方法信息
     * @param logInst 日志标识 控制是否把指令执行信息打印到控制台
     */
    public static void interpret(MtMethod method, boolean logInst) {
        try {
            // 创建线程实例
            MtThread thread = new MtThread();
            MtFrame frame = thread.newFrame(method);
            thread.pushFrame(frame);
            loop(thread, logInst);
        } catch (Exception e) {
            log.error("执行异常: {}", e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 循环执行
     *
     * @param thread  当前线程
     * @param logInst 控制是否把指令执行信息打印到控制台
     */
    private static void loop(MtThread thread, boolean logInst) {
        BytecodeReader reader = new BytecodeReader();
        try {
            do {
                // 取出顶部栈帧
                MtFrame frame = thread.currentFrame();
                // 获取下一个计数器
                int pc = frame.getNextPc();
                thread.setPc(pc);
                // 解码
                reader.reset(frame.getMethod().getCode(), pc);
                // 操作码
                short opCode = reader.readU1ToUnsignedByte();
                // 获取指令
                Instruction instruction = InstructionFactory.newInstruction(opCode);
                // 加载操作数
                instruction.fetchOperands(reader);
                // 设置下一步
                frame.setNextPc(reader.getPc());
                if (logInst) {
                    // 执行
                    logInstruction(frame, instruction);
                }
                instruction.execute(frame);
            } while (!thread.isStackEmpty());
        } catch (Exception e) {
            e.printStackTrace();
            logFrames(thread);
        }

    }

    /**
     * 指令打印
     *
     * @param frame       栈帧
     * @param instruction 指令
     */
    private static void logInstruction(MtFrame frame, Instruction instruction) {
        MtMethod method = frame.getMethod();
        String className = method.getMtClass().getName();
        String methodName = method.getName();
        int pc = frame.getThread().getPc();
        System.out.println(className + "." + methodName + "() #" + pc + " " + instruction);
    }

    /**
     * 打印日志帧
     *
     * @param thread 线程
     */
    private static void logFrames(MtThread thread) {
        while (!thread.isStackEmpty()) {
            MtFrame frame = thread.popFrame();
            MtMethod method = frame.getMethod();
            String className = method.getMtClass().getName();
            System.out.println(">> pc: " + frame.getNextPc() + " " + className + "." + method.getName() + method.getDescriptor());
        }
    }
}













